package com.example.capsule_netty.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.capsule_netty.bean.*;
import com.example.capsule_netty.configuration.Constant;
import com.example.capsule_netty.configuration.KafkaConstant;
import com.example.capsule_netty.global.GlobalThreadPool;
import com.example.capsule_netty.global.UserAndChannelRelevance;
import com.example.capsule_netty.mapper.FunctionMapper;
import com.example.capsule_netty.mapper.OneToOneMessageMapper;
import com.example.capsule_netty.mapper.UserInfoMapper;
import com.example.capsule_netty.mapper.UserToImeiBeanMapper;
import com.example.capsule_netty.protocol.MessageBeanProtocol;
import com.google.gson.Gson;
import io.netty.channel.Channel;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.SendResult;
import org.springframework.stereotype.Service;
import org.springframework.util.concurrent.ListenableFutureCallback;

import javax.annotation.Resource;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author: zhuzhiming
 * @Date: 2022/5/5 21:30
 */
@Service
@Slf4j
public class UserInfoService {

    @Resource
    UserInfoMapper userInfoMapper;

    @Autowired
    RedisTemplate<String, String> redisTemplate;

    @Autowired
    KafkaTemplate<String, String> kafkaTemplate;

    @Autowired
    UserAndChannelRelevance userAndChannelRelevance;

    @Autowired
    UserToImeiBeanMapper userToImeiBeanMapper;

    @Resource
    OneToOneMessageMapper oneToOneMessageMapper;

    @Resource
    FunctionMapper functionMapper;

    public String getUserInfoByUserId(String userId) {

        if (userId == null || userId.equals("")) {
            return new Gson().toJson(new UserInfoBean());
        }

        if (redisTemplate.opsForValue().get(userId) != null) {
            return redisTemplate.opsForValue().get(userId);
        }
        int id = Integer.parseInt(userId);
        UserInfoBean userInfoBean = userInfoMapper.selectById(id);
        if (userInfoBean == null) {
            int[] avatarIds = new int[]{2131165296, 2131165299, 2131165314, 2131165303, 2131165301, 2131165300, 2131165297, 2131165304, 2131165306, 2131165307};
            int index = new Random().nextInt(10);
            userInfoBean = new UserInfoBean();
            userInfoBean.setType(Constant.SAVE_USER);
            userInfoBean.setId(Integer.parseInt(userId));
            String imei = userToImeiBeanMapper.getImeiByUserId(userId);
            userInfoBean.setImei(imei);
            userInfoBean.setFriendApply("");
            userInfoBean.setFriends("");
            userInfoBean.setFieldRecord("");
            userInfoBean.setOnlineStatus(1);
            userInfoBean.setNickname("新用户" + UUID.randomUUID().toString().substring(0, 11));
            userInfoBean.setAvatarUrl(String.valueOf(avatarIds[index]));
            // userInfoBean.setAvatarServerPath(String.valueOf(avatarIds[index]));
            UserInfoBean finalUserInfoBean = userInfoBean;
            GlobalThreadPool.buildThreadPool().execute(new Runnable() {
                @Override
                public void run() {
                    kafkaTemplate.send(KafkaConstant.USER_TOPIC, new Gson().toJson(finalUserInfoBean)).addCallback(new ListenableFutureCallback<SendResult<String, String>>() {
                        @Override
                        public void onFailure(Throwable ex) {
                            log.info("send save userInfo message fail ------->" + ex);
                            kafkaTemplate.send(KafkaConstant.USER_TOPIC, new Gson().toJson(finalUserInfoBean));
                        }

                        @Override
                        public void onSuccess(SendResult<String, String> result) {
                            log.info("send save userInfo message success");
                        }
                    });
                }
            });
        }

        String userInfoJson = new Gson().toJson(userInfoBean);
        GlobalThreadPool.buildThreadPool().execute(new Runnable() {
            @Override
            public void run() {
                redisTemplate.opsForValue().set(userId, userInfoJson, 30, TimeUnit.MINUTES);
            }
        });

        return userInfoJson;

    }

    public String updateUserInfo(UserInfoBean userInfoBean) {

        if (userInfoBean == null) {
            return Constant.FAIL;
        }

        GlobalThreadPool.buildThreadPool().execute(new Runnable() {
            @Override
            public void run() {
                userInfoBean.setType(Constant.UPDATE_USER);
                kafkaTemplate.send(KafkaConstant.USER_TOPIC, new Gson().toJson(userInfoBean)).addCallback(new ListenableFutureCallback<SendResult<String, String>>() {
                    @Override
                    public void onFailure(Throwable ex) {
                        log.info("send update userinfo message fail ------->" + ex);
                    }

                    @Override
                    public void onSuccess(SendResult<String, String> result) {
                        //     log.info("send update userinfo message success");
                    }
                });
            }
        });

        return Constant.SUCCESS;


    }

    public String uploadAvatar(MessageBean messageBean) {

        String avatarServerPath = Constant.AVATAR_PATH + messageBean.getFilePath().substring(messageBean.getFilePath().lastIndexOf("/") + 1);
        GlobalThreadPool.buildThreadPool().execute(new Runnable() {
            @Override
            public void run() {
                if (!new File(Constant.AVATAR_PATH).exists()) {
                    new File(Constant.AVATAR_PATH).mkdirs();
                }

                try {
                    byte[] by = Base64.getDecoder().decode(messageBean.getContent());
                    OutputStream out = new FileOutputStream(avatarServerPath);
                    BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(out);
                    bufferedOutputStream.write(by);
                    bufferedOutputStream.flush();
                    bufferedOutputStream.close();
                    out.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        userInfoMapper.updateAvatar(messageBean.getSenderId(), avatarServerPath);
        return avatarServerPath;
    }


    @SneakyThrows
    public void addFieldRecord(String userId, String field) {

        UserInfoBean userInfoBean = userInfoMapper.selectById(userId);
        String fieldRecords = "";

        if (userInfoBean == null) {
            Thread.sleep(50);
            userInfoBean = userInfoMapper.selectById(userId);
            if (userInfoBean == null) {
                Thread.sleep(50);
                userInfoBean = userInfoMapper.selectById(userId);
            }
        }

        if (userInfoBean == null) {
            return;
        }
        if (userInfoBean.getFieldRecord() != null) {
            String[] fieldRecordArr = userInfoBean.getFieldRecord().split(",");
            for (String fieldRecord : fieldRecordArr) {
                if (fieldRecord.equals(field)) {
                    return;
                }
            }
            fieldRecords = userInfoBean.getFieldRecord();
            if (fieldRecordArr.length > 20) {
                fieldRecords = fieldRecords.substring(fieldRecords.indexOf(",") + 1);
            }
        }


        fieldRecords += field + ",";
        userInfoBean.setFieldRecord(fieldRecords);

        userInfoMapper.updateById(userInfoBean);
    }

    @SneakyThrows
    public String doGetFriends(String userId) {

        UserInfoBean userInfoBean = userInfoMapper.selectById(userId);

        if (userInfoBean == null) {
            Thread.sleep(50);
            userInfoBean = userInfoMapper.selectById(userId);
            if (userInfoBean == null) {
                Thread.sleep(50);
                userInfoBean = userInfoMapper.selectById(userId);
            }
        }

        if (userInfoBean == null || userInfoBean.getFriends() == null) {
            return new Gson().toJson(new ArrayList<UserInfoBean>());
        }
        String[] friends = userInfoBean.getFriends().split(",");
        List<UserInfoBean> friendList = new ArrayList<>();
        if (friends[0].equals("")) {
            return new Gson().toJson(friendList);
        }

        if (friends.length > 10) {
            int size = friends.length / 5;
            CountDownLatch lock = new CountDownLatch(5);
            for (int i = 0; i < 5; i++) {
                int finalI = i;
                GlobalThreadPool.buildThreadPool().execute(new Runnable() {
                    @Override
                    public void run() {
                        for (int j = finalI * size; j < (finalI == 4 ? friends.length : finalI * size + size); j++) {
                            UserInfoBean bean = userInfoMapper.selectById(friends[j]);
                            fillFriendList(userId, bean);
                            synchronized (this) {
                                friendList.add(bean);
                            }
                        }
                        lock.countDown();
                    }
                });
            }
            lock.await();
        } else {
            for (String otherId : friends) {
                UserInfoBean bean = userInfoMapper.selectById(otherId);
                fillFriendList(userId, bean);
                friendList.add(bean);
            }
        }

        friendList.sort(new Comparator<UserInfoBean>() {
            @Override
            public int compare(UserInfoBean o1, UserInfoBean o2) {
                return o2.getOnlineStatus() - o1.getOnlineStatus();
            }
        });
        return new Gson().toJson(friendList);
    }

    @SneakyThrows
    private void fillFriendList(String userId, UserInfoBean bean) {
        String roomName = Integer.parseInt(userId) + bean.getId() + "chatroom";
        QueryWrapper<OneToOneMessageBean> queryWrapper = new QueryWrapper<OneToOneMessageBean>().eq("room_name", roomName);
        List<OneToOneMessageBean> oneToOneMessageBeans = oneToOneMessageMapper.selectList(queryWrapper);

        AtomicInteger unSignCount = new AtomicInteger(0);

        if (oneToOneMessageBeans.size() > 30) {
            int step = oneToOneMessageBeans.size() / 5;
            CountDownLatch countDownLatch = new CountDownLatch(5);
            for (int i = 0; i < 5; i++) {
                int finalI = i;
                GlobalThreadPool.buildThreadPool().execute(() -> {
                    try {
                        for (int j = finalI * step; j < (finalI == 4 ? oneToOneMessageBeans.size() : finalI * step + step); j++) {
                            if (oneToOneMessageBeans.get(j).getSignStatus() == 0) {
                                unSignCount.incrementAndGet();
                            }
                        }
                    } catch (Exception e) {
                        log.error(e.toString());
                    } finally {
                        countDownLatch.countDown();
                    }
                });
            }
            countDownLatch.await();
        } else {
            for (OneToOneMessageBean oneToOneMessageBean : oneToOneMessageBeans) {
                if (oneToOneMessageBean.getSignStatus() == 0) {
                    unSignCount.incrementAndGet();
                }
            }
        }

        bean.setNewMessageCount(unSignCount.get());

        if (oneToOneMessageBeans.size() > 0) {
            OneToOneMessageBean lastMessage = oneToOneMessageBeans.get(oneToOneMessageBeans.size() - 1);

            if (lastMessage.getSignStatus() == 2) {
                if (userId.equals(lastMessage.getSenderId())) {
                    bean.setLastMessage("您撤回了一条消息");
                } else {
                    bean.setLastMessage("对方撤回了一条消息");
                }

            } else {
                switch (lastMessage.getMsgType()) {
                    case 0:
                        if (lastMessage.getContent() != null) {
                            bean.setLastMessage(lastMessage.getContent().length() > 10 ? lastMessage.getContent().substring(0, 10) + "..." : lastMessage.getContent());
                        } else {
                            bean.setLastMessage("");
                        }
                        break;
                    case 1:
                        bean.setLastMessage("[图片]");
                        break;
                    case 2:
                        bean.setLastMessage("[文件]");
                        break;
                    case 3:
                        bean.setLastMessage("[视频]");
                        break;
                    default:
                        bean.setLastMessage("");
                }
            }


        } else {
            bean.setLastMessage("");
        }
        int onlineStatus = userAndChannelRelevance.getOnlineStatus(bean.getImei());
        bean.setOnlineStatus(onlineStatus);
    }

    public String doAddFriend(MessageBean messageBean) {

        String senderId = messageBean.getSenderId();
        String receiverId = messageBean.getReceiverId();
        UserInfoBean userInfoBean = userInfoMapper.selectById(receiverId);

        if (userInfoBean == null) {
            messageBean.setStatus(3);
            return new Gson().toJson(messageBean);
        }
        final String[] friendApply = {userInfoBean.getFriendApply()};

        if (friendApply[0].contains(senderId)) {
            messageBean.setStatus(0);
            return new Gson().toJson(messageBean);
        }

        CompletableFuture.runAsync(() -> {
            friendApply[0] = friendApply[0] + senderId + ",";
            userInfoBean.setFriendApply(friendApply[0]);
            String friendApplyInstruction = userInfoBean.getFriendApplyInstruction();
            friendApplyInstruction = friendApplyInstruction + messageBean.getContent() + "->";
            userInfoBean.setFriendApplyInstruction(friendApplyInstruction);
            userInfoMapper.updateById(userInfoBean);

        });

        Channel channel = userAndChannelRelevance.getChannelFromImei(userInfoBean.getImei());

        if (channel == null) {
            messageBean.setStatus(0);
            return new Gson().toJson(messageBean);
        }

        messageBean.setMsgType(10);
        messageBean.setStatus(1);
        messageBean.setOneToOne(true);
        String messageJson = new Gson().toJson(messageBean);
        MessageBeanProtocol messageBeanProtocol = new MessageBeanProtocol();
        messageBeanProtocol.setContent(messageJson.getBytes(StandardCharsets.UTF_8));
        messageBeanProtocol.setLen(messageJson.getBytes(StandardCharsets.UTF_8).length);
        channel.writeAndFlush(messageBeanProtocol);

        return messageJson;

    }

    @SneakyThrows
    public String getFriendFunctionList(String userId) {

        CompletableFuture<List<FunctionBean>> functionList = CompletableFuture.supplyAsync(() -> {
            QueryWrapper<FunctionBean> queryWrapper = new QueryWrapper<FunctionBean>().eq("is_one_to_one", 1);
            return functionMapper.selectList(queryWrapper);
        });

        CompletableFuture<List<FriendApplyBean>> applyList = CompletableFuture.supplyAsync(() -> {

            UserInfoBean userInfoBean = userInfoMapper.selectById(userId);
            List<FriendApplyBean> friendApplyList = new ArrayList<>();

            if (userInfoBean == null) {
                try {
                    Thread.sleep(50);
                    userInfoBean = userInfoMapper.selectById(userId);
                    if (userInfoBean == null) {
                        Thread.sleep(50);
                        userInfoBean = userInfoMapper.selectById(userId);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            if(userInfoBean == null){
                return friendApplyList;
            }

            String apply = userInfoBean.getFriendApply();

            if (apply.equals("")) {
                return friendApplyList;
            }

            String[] applies = userInfoBean.getFriendApply().split(",");
            String[] instructions = userInfoBean.getFriendApplyInstruction().split("->");

            for (int i = 0; i < applies.length; i++) {
                FriendApplyBean friendApply = new FriendApplyBean();
                String friendRedisCache = redisTemplate.opsForValue().get(applies[i]);
                if (friendRedisCache == null) {
                    userInfoBean = userInfoMapper.selectById(applies[i]);
                } else {
                    userInfoBean = new Gson().fromJson(friendRedisCache, UserInfoBean.class);
                }
                friendApply.setFriendAvatarPath(userInfoBean.getAvatarUrl());
                friendApply.setApplyId(applies[i]);
                if (instructions.length > i) {
                    friendApply.setApplyInstruction(instructions[i]);
                } else {
                    friendApply.setApplyInstruction("");
                }
                friendApplyList.add(friendApply);
            }
            return friendApplyList;
        });

        CompletableFuture<List<FunctionBean>> functions = applyList.thenCombine(functionList, (friendApplies, functionBeans) -> {
            for (FunctionBean functionBean : functionBeans) {
                if (functionBean.getFunctionName().equals("好友申请")) {
                    functionBean.setApplyList(friendApplies);
                    break;
                }
            }
            return functionBeans;
        });

        return new Gson().toJson(functions.get());
    }

    public String dealFriendApply(MessageBean messageBean) {

        String senderId = messageBean.getSenderId();
        String receiverId = messageBean.getReceiverId();
        int status = messageBean.getStatus();
        UserInfoBean userInfoBean;
        userInfoBean = userInfoMapper.selectById(senderId);
        String[] applies = userInfoBean.getFriendApply().split(",");
        String[] instructions = userInfoBean.getFriendApplyInstruction().split("->");

        StringBuilder applySb = new StringBuilder();
        StringBuilder instructionSb = new StringBuilder();

        if (status == 1) {
            String friends = userInfoBean.getFriends();
            friends += receiverId + ",";
            userInfoBean.setFriends(friends);
            CompletableFuture.runAsync(() -> {
                signFriend(messageBean);
            });
        }
        for (int i = 0; i < applies.length; i++) {

            if (!applies[i].equals(receiverId)) {
                applySb.append(applies[i]).append(",");
                instructionSb.append(instructions[i]).append("->");
            }
        }
        userInfoBean.setFriendApply(applySb.toString());
        userInfoBean.setFriendApplyInstruction(instructionSb.toString());

        userInfoMapper.updateById(userInfoBean);

        return Constant.SUCCESS;

    }

    private void signFriend(MessageBean messageBean) {

        String userJson = redisTemplate.opsForValue().get(messageBean.getReceiverId());
        UserInfoBean userInfoBean;

        if (userJson != null) {
            userInfoBean = new Gson().fromJson(userJson, UserInfoBean.class);
        } else {
            userInfoBean = userInfoMapper.selectById(messageBean.getReceiverId());
        }

        CompletableFuture.runAsync(() -> {
            String friends = userInfoBean.getFriends();
            friends += messageBean.getSenderId() + ",";
            userInfoBean.setFriends(friends);
            userInfoMapper.updateById(userInfoBean);
        });

        Channel channel = userAndChannelRelevance.getChannelFromImei(userInfoBean.getImei());

        if (channel != null) {
            messageBean.setStatus(4);
            messageBean.setOneToOne(true);
            MessageBeanProtocol messageBeanProtocol = new MessageBeanProtocol();
            String messageBeanJson = new Gson().toJson(messageBean);
            messageBeanProtocol.setContent(messageBeanJson.getBytes(StandardCharsets.UTF_8));
            messageBeanProtocol.setLen(messageBeanJson.getBytes(StandardCharsets.UTF_8).length);
            channel.writeAndFlush(messageBeanProtocol);
        }

    }

}
